package com.toilet.easyvideo.api.controller;

import com.toilet.easyvideo.config.FileConfig;
import com.toilet.easyvideo.constant.FileUploadEnum;
import com.toilet.easyvideo.constant.StringConstant;
import com.toilet.easyvideo.domain.UploadFile;
import com.toilet.easyvideo.exception.ServiceException;
import com.toilet.easyvideo.response.*;
import com.toilet.easyvideo.service.FileService;
import com.toilet.easyvideo.util.SecurityUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * 文件操作controller
 *
 * @author snowfrake
 * @date 2023/11/3
 */
@Api("文件操作")
@RestController
@RequestMapping("/operation_file")
@Slf4j
public class FileController {
    @Resource
    private FileService fileService;
    @Resource
    private FileConfig fileConfig;

    @ApiOperation("获取上传的文件块大小")
    @GetMapping("/file_chunk_size")
    public Integer fileChunkSize() {
        return fileConfig.getChunkSize().intValue();
    }

    @ApiOperation("文件上传检查")
    @GetMapping("/file_check")
    public FileCheckResponse fileCheck(@RequestParam @ApiParam("文件md5值") String md5) {
        log.info("检查MD5:" + md5);
        FileCheckResponse response = new FileCheckResponse();
        //首先检查是否有完整的文件
        Boolean isUploaded = fileService.selectFileByMd5(md5);
        if (Objects.equals(isUploaded, Boolean.FALSE)) {
            //如果没有，就查找分片信息，并返回给前端
            List<Long> chunkList = fileService.selectChunkListByMd5(md5);
            response.setUploadResult(false);
            response.setChunkList(chunkList);
            return response;
        }
        //如果有，就返回已经上传完毕了
        response.setUploadResult(true);
        return response;
    }

    //todo 文件分片上传https://blog.csdn.net/weixin_50799082/article/details/128547482
    //todo 后续修复 .tar.gz的后缀会存在问题
    @ApiOperation("上传文件片")
    @PostMapping("/file_chunk_upload")
    public FileUploadResponse fileChunkUpload(@RequestParam @ApiParam("文件md5值") String md5,
                                              @RequestParam @ApiParam("文件片序号") Long chunkNumber,
                                              @RequestParam @ApiParam("文件片大小") Long chunkSize,
                                              @RequestParam @ApiParam("文件总数量") Long chunkTotal,
                                              @RequestParam @ApiParam("文件大小") Long fileSize,
                                              @RequestParam @ApiParam("文件名称") String fileName,
                                              @RequestBody @ApiParam("文件片数据") byte[] fileContent) {
        log.info("上传文件片md5值:{},文件总数量为：{},文件片当前序号：{},文件块：{},接收的文件大小为：{}",
                md5, chunkTotal, chunkNumber, chunkSize, fileContent.length);
        // todo 后续通过代码识别当前系统来获取对应的文件后缀
        String[] splits = fileName.split("\\.");
        String type = splits[splits.length - 1];
        String resultFileName = fileConfig.getUploadPath() + md5 + "." + type;
        fileService.saveChunk(fileContent, md5, chunkNumber, chunkSize, resultFileName, SecurityUtils.getUserId());
        FileUploadResponse response = new FileUploadResponse();
        if (Objects.equals(chunkNumber, chunkTotal)) {
            Integer addFile = fileService.addFile(fileName, md5, fileSize, SecurityUtils.getUserId());
            if (addFile > 0) {
                fileService.deleteChunkByMd5(md5, SecurityUtils.getUserId());
            }
            response.setUploadResult(Boolean.TRUE);
            response.setUploadChunk(FileUploadEnum.FILE_UPLOAD_CHECK_FAIL.getCode());
        } else {
            //文件分片上传成功
            response.setUploadResult(Boolean.TRUE);
            response.setUploadChunk(FileUploadEnum.FILE_UPLOAD_SUCCESS.getCode());
        }
        return response;
    }

    @ApiOperation("文件列表分页查询")
    @GetMapping("/file_list")
    public BasePageResponse<FileResponse> fileList(@RequestParam @ApiParam("页码值") Long pageSize,
                                                   @RequestParam @ApiParam("当前页") Long currentPage) {
        return fileService.fileList(pageSize, currentPage, SecurityUtils.getUserId());
    }

    @ApiOperation("文件分片下载")
    @GetMapping("/file_stream_download")
    public void fileStreamDownload(@RequestParam @ApiParam("文件id") Long fileId,
                                   @RequestParam @ApiParam("当前大小") Long currentSize,
                                   @RequestParam @ApiParam("下载大小") Long chunkSize,
                                   HttpServletResponse response) {
        if (chunkSize > fileConfig.getChunkSize()) {
            throw new ServiceException("文件块过大,无法完成请求!");
        }
        DownLoadFileResponse fileResponse = fileService.selectFileById(fileId, currentSize, chunkSize);
        log.info("下载文件分片大小{}", fileResponse.getChunk().length);

        response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileResponse.getFileName(), StandardCharsets.UTF_8));
        response.addHeader("Content-Length", "" + fileResponse.getChunk().length);
        response.setHeader("filename", URLEncoder.encode(fileResponse.getFileName(), StandardCharsets.UTF_8));
        response.setContentType("application/octet-stream");
        //返回读取字节数组
        ServletOutputStream out = null;
        try {
            out = response.getOutputStream();
            out.write(fileResponse.getChunk());
            out.flush();
            out.close();
        } catch (IOException e) {
            log.warn("下载文件分片失败", e);
        }
    }

    @ApiOperation("移除文件")
    @GetMapping("/delete_by_file")
    public Boolean deleteByFile(@RequestParam @ApiParam("文件id") Long fileId) {
        return fileService.deleteFileById(fileId, SecurityUtils.getUserId());
    }
}
